//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <regs.h>

// display exception code
#if defined(_DEBUG)
#define __display_exception_code            \
    .set noreorder;                         \
    mfc0    k1, CP0_CAUSE;                  \
    nop;                                    \
    .set reorder;                           \
    li      k0, 0xb80000f0;                 \
    andi    k1, k1, 0x7c;                   \
    srl     k1, 2;                          \
    sb      k1, (k0);
#else
#define __display_exception_code
#endif

LEAF(RestorAndRet)
    RESTORE_ALL
    ERET
END(RestorAndRet)

IMPORT(exception_handlers,32 * 4);

NESTED_ENTRY(ExceptGeneric, 0, sp)
    __display_exception_code
    .set noreorder;
    mfc0    k1, CP0_CAUSE
    nop
    .set reorder;

    la      k0, uExceptionHandlers
    andi    k1, k1, 0x7c
    addu    k0, k0, k1
    lw      k0, (k0)
    jr      k0
END(ExceptGeneric)

NESTED_ENTRY(ExceptTlb, 0, sp)
    __led_display(0xff)
    la  k0, ExceptTlbFill;
    jr  k0;
END(ExceptTlb)

NESTED_ENTRY(ExceptTlbFill, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    or      a1, sp, zero;
    addiu   sp, sp , -0x10;
    .set    noreorder;
    mfc0    a0, CP0_BADVADDR;
    nop;
    .set    reorder;
    jal     FillTlb;
    addiu   sp, sp , 0x10;

    __clean_led();

    j       RestorAndRet
END(ExceptTlbFill)

NESTED_ENTRY(ExceptCache, 0, sp)
    // disable cache
    .set noreorder
    mfc0    k0, CP0_CONFIG
    li      k1, ~CONF_CM_CMASK
    and     k0, k0, k1
    ori     k0, k0, CONF_CM_UNCACHED
    mtc0    k0, CP0_CONFIG
    .set reorder
    __led_display(0xfe);
    j       CacheHandler
END(ExceptCache)

NESTED_ENTRY(BpHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp               // pCxt
    addiu   sp, sp , -0x10;
    jal     DoBpHandle
    addiu   sp, sp , 0x10;

    j       RestorAndRet
END(BpHandler)

NESTED_ENTRY(RiHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoRI                // Outside reservtion instruction routine
    addiu   sp, sp , 0x10;

    __clean_led();

    j       RestorAndRet
END(RiHandler)

// TODO(mips): It's maybe merge with TlbHandler
NESTED_ENTRY(TlbHandlerDouble, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;

    .set noreorder;
    mfc0    a1, CP0_BADVADDR
    nop
    jal     DoTlbDoubleFault
    nop
    .set reorder;

    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(TlbHandlerDouble)

NESTED_ENTRY(CacheHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0,sp
    addiu   sp, sp , -0x10;
    jal     DoCacheError
    addiu   sp, sp, 0x10;
    j       RestorAndRet
END(CacheHandler)

NESTED_ENTRY(GenericHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoGenericException
    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(GenericHandler)

NESTED_ENTRY(FpuHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoFpuException
    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(FpuHandler)

NESTED_ENTRY(CopHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoCopException
    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(CopHandler)

NESTED_ENTRY(TLBModHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoTLBModException;
    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(TLBModHandler)

NESTED_ENTRY(AdeHandler, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoAdeException;
    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(AdeHandler)


.extern g_uNumberOfSysCall
.extern g_fnSystemCalls

NESTED_ENTRY(SysHandler, 0, sp)
#ifdef _DEBUG
    lui     k0, 0x8000;
    subu    k1, sp, k0;
    bgez    k1, 5f;
#endif

    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

#ifdef _DEBUG
    lw      k0, PT_R29(sp)  // get the old sp
    bne     t8, k0, 1f;     // t8 keep the old sp
    lui     k0, 0x8000;
    subu    k1, t8, k0;
    bgez    k1, 1f;
#endif

    la      t0, g_uNumberOfSysCall;
    lw      t1, 0(t0);

    subu    t1, t1, v0;
    bgez    t1, 2f;

    or      a0, sp, zero;
    addiu   sp, sp , -0x10;
    jal     DoSysCallException
    addiu   sp, sp , 0x10;
    b       3f;

2:
    lw      t0, PT_EPC(sp);
    addu    t0, t0, 4
    sw      t0, PT_EPC(sp);

    addiu   sp, sp, -0x20;

    .set    noreorder
    mfc0    t0, CP0_STATUS;
    lui     t1, 0xffff;
    ori     t2, t1, 0xfffd;     // ~SR_EXL
    and     t0, t0, t2;
    ori     t2, t1, 0xfffb;     // ~SR_ERL
    and     t0, t0, t2;
    ori     t2, t1, 0xffe7;     // exchange to the kernel mode
    and     t0, t0, t2;
    mtc0    t0, CP0_STATUS;
    nop;
    .set    reorder;

    li      t0, 0x5;
    subu    t1, t0, v1;
    bgtz    t1, 4f;

    // copy the parameter
    lw      t4, 0x10(t8);
    sw      t4, 0x10(sp);

4:
    la      t5, g_fnSystemCalls;
    sll     t0, v0, 2;
    addu    t1, t0, t5;
    lw      t1, (t1);
    jalr    t1;
    addiu   sp, sp, 0x20;

    sw      v0, PT_R2(sp);

    sw      zero, PT_CAUSE(sp);

3:
    __clean_led();

    j       RestorAndRet

#ifdef _DEBUG
1:
    li      s8, 0xffff;
    jal     Chat;
5:
    or      s5, sp, zero;
    .set    noreorder;
    mfc0    s6, CP0_STATUS;
    mfc0    s7, CP0_CAUSE;
    .set    reorder;
    or      s3, sp, zero;
    li      s8, 0x55555;
    jal     Chat;
#endif
END(SysHandler)

NESTED_ENTRY(IRQHandler, PT_SIZE, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoIrqDispatch;
ReturnToUserEntry:
    jal     MaybeSchedule;

    addiu   sp, sp, 0x10;           // restore sp , which sub in IRQHandler
    j       RestorAndRet;
END(IRQHandler)

LEAF(SystemThreadStartup)
    lw      $24, 20($29);               /*status*/
    .set    noreorder;
    mfc0    $25, $12;
    lui     $15, 0xffff;
    ori     $15, $15, 0x00ff;
    and     $24, $24, $15;
    andi    $25, $25, 0xff00;
    or      $24, $24, $25;
    mtc0    $24, $12;
    .set    reorder;
    jal     EnablePreemption;

    addiu   $29, $29, 20;

    lw      $5, 32($29);                /*a1*/
    lw      $4, 28($29);                /*a0*/
    lw      $31, 4($29);                /*ra*/
    addiu   $29, $29, 44;

    jr      $31;
END(SystemThreadStartup)

#ifdef _XINHAO
NESTED_ENTRY(MachineCheck, 0, sp)
    SWITCHINTSTACK(g_kernelStack);
    SAVE_ALL

    move    a0, sp
    addiu   sp, sp , -0x10;
    jal     DoMachineCheckException;
    addiu   sp, sp , 0x10;
    j       RestorAndRet
END(MachineCheck)
#endif

LEAF(ThreadStartup)
    lw      $24, 0x14($29);               /*status*/
    .set    noreorder;
    mfc0    $25, $12;
    lui     $15, 0xffff;
    ori     $15, $15, 0x00ff;
    and     $24, $24, $15;
    andi    $25, $25, 0xff00;
    or      $24, $24, $25;
    mtc0    $24, $12;
    .set    reorder;
    jal     EnablePreemption;

    addiu   $29, $29, 20;

    lw      $4, 28($29);                /*a0*/
    lw      $21, 4($29);                /* PoolThreadEntry */
    la      $31, ReturnToUserEntry;
    addiu   $29, $29, 44;

    jr      $21;
END(ThreadStartup)
